Nous avons collecté pas mal de données à propos des paquets R présents sur GitHub. En particulier, nous avons le flux d'activité de tous ces paquets (avec notamment les PushEvents) ainsi que des fichiers .csv contenant les fichiers DESCRIPTION de chaque package. Ces fichiers ont été collectés à la fois sur CRAN, sur BioConductor et sur GitHub.
Il serait intéressant de comparer l'activité de ces différents groupes de paquets.
Pour ce notebook, les fichiers suivants vont être utilisés :
In [1]:
import pandas
bioconductor = pandas.read_csv('../data/bioconductorsvn_description.csv')
cran = pandas.read_csv('../data/cran-description.csv')
rforge = pandas.read_csv('../data/rforge_description.csv')
r_forge = pandas.read_csv('../data/r-forge_description.csv')
github = pandas.read_csv('../data/github-description.csv').query('owner != "rpkg" and owner != "cran"')
events = pandas.read_csv('../data/github_R_pushevents.csv')
Le format de chacun de ces dataframes est un peu différent. cran
et github
nécessitent d'être pivotés, alors que events
contient des données d'une toute autre nature :
In [37]:
events.head()
Out[37]:
Ceux qui nécessitent juste un renommage sur le label des packages.
In [38]:
bioconductor = bioconductor.rename(columns={'Unnamed: 0': 'Pkg'}).set_index('Package', verify_integrity=True)
rforge = rforge.rename(columns={'Unnamed: 0': 'Pkg'}).set_index('Package')
r_forge = r_forge.rename(columns={'Unnamed: 0': 'Pkg'}).set_index('Package')
cran
doit être pivoté et nécessite également un renommage et un index sur le nom des packages. Le dataframe cran
contient également des informations pour plusieurs versions d'un même package. Par convention, nous ne garderons que la dernière information (i.e. la dernière version).
In [2]:
cran = cran.rename(columns={'package': 'Package'}).drop(labels=['version'], axis=1).drop_duplicates(['Package', 'key'], take_last=True).pivot(index='Package', columns='key', values='value')
Le dataframe github
est un peu différent, dans le sens où l'identification se fait via le couple (owner, repository)
et non pas via le nom du package. La première chose à faire est d'associer un nom de package à chaque dépôt. Ensuite, nous utiliserons (owner, repository)
comme index.
In [40]:
_ = github[github['key'] == 'Package'][['owner', 'repository', 'value']].rename(columns={'value': 'Package'})
_ = github.merge(_, on=('owner', 'repository')).drop_duplicates(['owner', 'repository', 'key'], take_last=True)
_['url'] = _['owner'] + '/' + _['repository']
github = _.pivot(index='url', columns='key', values='value')
Enfin, le dataframe events
sera utilisé ultérieurement afin de joindre les informations des autres dataframes. La colonne package
va être renommée afin de faciliter la jointure avec bioconductor
et cran
, alors qu'une nouvelle colonne url
va être construite afin de faciliter la jointure avec github
. Finalement, ce dataframe va être indexé sur base de la date de l'événement, renommée en date
.
In [41]:
_ = events.rename(columns={'package': 'Package', 'created_at': 'Date'})
_['url'] = _['owner'] + '/' + _['repository']
_['Date'] = pandas.to_datetime(_['Date'])
events = _.set_index('Date')
Nous savons par expérience qu'un pic d'activité inhabituel est observé en avril 2014. En réalité, ce pic correspond à une activité particulière liée aux dépôts appartenant à cran
et rpkg
sur GitHub. Ces deux owners servent en réalité à mirrorer les paquets qui sont présents sur CRAN, et ne représentent pas un développement de paquet ou quoi que ce soit réellement lié au développement de paquets. Nous allons donc extraire ces informations du flux d'événements.
In [42]:
events = events.query('owner != "rpkg" and owner != "cran"')
Chaque événement est associé à un dépôt GitHub ainsi qu'à un package.
Une remarque importante : l'association entre un événement et un package provient d'un pré-traitement en amont, dont le résultat a été stocké dans le .csv à la base de events
. Si, lors d'une reproduction de l'expérience courante, l'utilisateur souhaite repartir des données brutes (issues de MongoDB ou de GitHubArchive), il convient de faire une jointure avec le dataframe github
, sur base de url
afin d'associer un package au dépôt concerné par l'événement.
Grâce à cette association, il nous est possible d'identifier quels sont les packages qui appartiennent à telle ou telle communauté (ex. : CRAN, BioConductor ou GitHub exclusivement) et de mesurer les différences d'activité en fonction de ces communautés. La première étape consiste donc à identifier, pour chaque événement, si le dépôt relatif est lié à une (ou plusieurs) communautés.
In [43]:
# Tags data in each related data frame
cran['cran'] = 1
bioconductor['bioconductor'] = 1
rforge['rforge'] = 1
r_forge['r-forge'] = 1
events['all'] = 1
In [44]:
_ = events.merge(cran[['cran']], how='left', left_on='Package', right_index=True)
_ = _.merge(bioconductor[['bioconductor']], how='left', left_on='Package', right_index=True)
_ = _.merge(rforge[['rforge']], how='left', left_on='Package', right_index=True)
_ = _.merge(r_forge[['r-forge']], how='left', left_on='Package', right_index=True)
events = _.fillna(value=0)
In [45]:
# Tag events that are related only to github
events['github only'] = events.apply(lambda x: 1 - max(x['cran'], x['bioconductor'], x['rforge']), axis=1)
In [46]:
%matplotlib inline
columns = ['cran', 'bioconductor', 'rforge', 'r-forge', 'github only', 'all']
_ = pandas.stats.moments.rolling_sum(events[columns], 30, freq='1D', how='sum')
_.plot(figsize=(15, 6), title=u'# événements "PushEvent" par type de packages')
Out[46]:
Nous pouvons également nous intéresser à la création de dépôts pour chaque catégorie de packages. En nous basant sur les PushEvent, nous pouvons supposer que la création du dépôt correspond au premier PushEvent pour chaque package.
Notons que le fait d'utiliser le Package
et non (owner
, repository
) nous permet de filtrer les forks.
In [47]:
events_first = events.drop_duplicates(('Package',))
_1 = pandas.stats.moments.rolling_sum(events_first[columns], 4, freq='1W', how='sum')
_1.plot(figsize=(15, 6), title=u'# de création de dépôts par type de packages')
Out[47]:
Afin de se faire une meilleure idée du comportement entre la communauté CRAN et la communauté GitHubOnly, nous pouvons représenter le ratio entre le nombre d'événements de chaque communauté.
In [48]:
_1['First events ratio'] = _1['github only'] / _1['cran']
ax = _1[['First events ratio']].plot(figsize=(15, 6))
_2 = pandas.stats.moments.rolling_sum(events[columns], 30, freq='1D', how='sum')
_2['All events ratio'] = _2['github only'] / _2['cran']
_2[['All events ratio']].plot(figsize=(15, 6), ax=ax, title=u'Ratio entre GitHub et CRAN')
Out[48]:
In [49]:
github_set = set(github['Package'])
cran_set = set(cran.index)
r_forge_set = set(r_forge.index)
rforge_set = set(rforge.index)
bioconductor_set = set(bioconductor.index)
In [51]:
from matplotlib_venn import venn2, venn3
from matplotlib import pyplot as plt
figure, axes = plt.subplots(1, 2, figsize=(15,6))
venn3((github_set, cran_set, bioconductor_set), ('github', 'cran', 'bioconductor'), ax=axes[0])
venn3((github_set, cran_set, r_forge_set), ('github', 'cran', 'r-forge'), ax=axes[1])
Out[51]: